/*********************************************************************************
  *Copyright (c)    2021   xldeng
  *FileName:        log.cc.c
  *Author:          xldeng
  *Version:         1.0
  *Date:            2021/2/24 11:17
  *Description:     日志模块
  *Others:
  *Function List:
     1.…………
  *History:
     1.Date:
       Author:
       Modification:
**********************************************************************************/
#include <iostream>
#include <map>
#include <functional>
#include <cstdarg>
#include <ctime>
#include <cstring>
#include "util.h"
#include "log.h"
#include "config.h"
#include "macro.h"

namespace sylar {
    const char *LogLevel::ToString(LogLevel::Level level) {
        switch (level) {
#define XX(name)\
            case LogLevel::Level::name:\
                return #name;\
                break;
            XX(DEBUG);
            XX(INFO);
            XX(WARN);
            XX(ERROR);
            XX(FATAL);
#undef XX
            default:
                return "UNKNOWN";
        }
    }

    LogLevel::Level LogLevel::FromString(const std::string &str) {
#define XX(level, v)\
        if (str == #v){\
            return LogLevel::level;\
        }
        XX(DEBUG, debug);
        XX(INFO, info);
        XX(WARN, warn);
        XX(ERROR, error);
        XX(FATAL, fatal);
        XX(DEBUG, DEBUG);
        XX(INFO, INFO);
        XX(WARN, WARN);
        XX(ERROR, ERROR);
        XX(FATAL, FATAL);
        return LogLevel::UNKNOWN;
#undef XX
    }

    LogEventWrap::~LogEventWrap() {
        m_event->getLogger()->log(m_event->getLevel(), m_event);
    }

    std::stringstream &LogEventWrap::getSS() {
        return m_event->getSS();
    }

    LogEvent::LogEvent(std::shared_ptr<Logger> logger, LogLevel::Level level, const char *file, int32_t line,
                       uint32_t elapse, uint32_t thread_id, uint32_t fiber_id, uint64_t time,
                       const std::string &thread_name)
            : m_file(file), m_line(line), m_elapse(elapse), m_threadId(thread_id), m_fiberId(fiber_id),
              m_time(time), m_threadName(thread_name), m_logger(logger), m_level(level) {}

    void LogEvent::format(const char *fmt, ...) {
        va_list al;
        va_start(al, fmt);
        format(fmt, al);
        va_end(al);
    }

    void LogEvent::format(const char *fmt, va_list al) {
        char *buf = nullptr;
        int len = vasprintf(&buf, fmt, al);
        if (len != -1) {
            m_ss << std::string(buf, len);
            free(buf);
        }
    }

    void LogAppender::setFormatter(LogFormatter::ptr formatter) {
        MutexType::Lock lock(m_mutex);
        m_formatter = formatter;
        if (m_formatter) {
            m_hasFormatter = true;
        } else {
            m_hasFormatter = false;
        }
    }

    LogFormatter::ptr LogAppender::getFormatter() {
        MutexType::Lock lock(m_mutex);
        return m_formatter;
    }


    Logger::Logger(const std::string &name) : m_name(name), m_level(LogLevel::DEBUG) {
        m_logFormatter.reset(new LogFormatter("%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"));

    }

    void Logger::log(LogLevel::Level level, LogEvent::ptr event) {
        if (level >= m_level) {
            MutexType::Lock lock(m_mutex);
            if (!m_appenders.empty()) {
                for (auto &appender : m_appenders) {
                    appender->log(level, event);
                }
            } else if (m_root) {
                m_root->log(level, event);
            }
        }
    }

    void Logger::debug(LogEvent::ptr event) { log(LogLevel::DEBUG, event); }

    void Logger::info(LogEvent::ptr event) { log(LogLevel::INFO, event); }


    void Logger::warn(LogEvent::ptr event) { log(LogLevel::WARN, event); }

    void Logger::error(LogEvent::ptr event) { log(LogLevel::ERROR, event); }

    void Logger::fatal(LogEvent::ptr event) { log(LogLevel::FATAL, event); }

    void Logger::addAppender(LogAppender::ptr appender) {
        MutexType::Lock lock(m_mutex);
        if (!appender->getFormatter()) {
            MutexType::Lock ll(appender->m_mutex);
            // 使用友元绕过setFormatter()函数对m_hasformatter的改变
            appender->m_formatter = m_logFormatter;
        }
        m_appenders.push_back(appender);
    }

    void Logger::delAppender(LogAppender::ptr appender) {
        MutexType::Lock lock(m_mutex);
        for (auto it = m_appenders.begin(); it != m_appenders.end(); it++) {
            if (*it == appender) {
                m_appenders.erase(it);
                break;
            }
        }
    }

    void Logger::clearAppenders() {
        MutexType::Lock lock(m_mutex);
        m_appenders.clear();
    }

    std::string Logger::toYamlString() {
        MutexType::Lock lock(m_mutex);
        YAML::Node node;
        node["name"] = m_name;
        if (m_level != LogLevel::UNKNOWN) {
            node["level"] = LogLevel::ToString(m_level);
        }
        if (m_logFormatter) {
            node["formatter"] = m_logFormatter->getPattern();
        }
        for (auto &i:m_appenders) {
            node["appenders"].push_back(YAML::Load(i->toYamlString()));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }

    void Logger::setFormatter(LogFormatter::ptr val) {
        MutexType::Lock lock(m_mutex);
        m_logFormatter = val;
        for (auto &i : m_appenders) {
            MutexType::Lock ll(i->m_mutex);
            // 友元
            if (!i->m_hasFormatter) {
                i->m_formatter = m_logFormatter;
            }
        }
    }

    void Logger::setFormatter(const std::string &val) {
//        std::cout << "---" << val << std::endl;
        LogFormatter::ptr new_val(new LogFormatter(val));
        if (new_val->isError()) {
            std::cout << "Logger setFormatter name =" << m_name
                      << " value=" << val << " invalid formatter" << std::endl;
            return;
        }
        setFormatter(new_val);
    }

    LogFormatter::ptr Logger::getFormatter() {
        MutexType::Lock lock(m_mutex);
        return m_logFormatter;
    }

    void StdoutLogAppender::log(LogLevel::Level level, LogEvent::ptr event) {
        if (level >= m_level) {
            MutexType::Lock lock(m_mutex);
            std::cout << m_formatter->format(level, event);
        }
    }

    FileLogAppender::FileLogAppender(const std::string &filename) : m_filename(filename) {
        reopen();
    }

    void FileLogAppender::log(LogLevel::Level level, LogEvent::ptr event) {
        if (level >= m_level) {
            uint64_t now = event->getTime();
            if (now >= (m_lastTime + 3)) {
                reopen();
                m_lastTime = now;
            }
            MutexType::Lock lock(m_mutex);
            //if(!(m_filestream << m_formatter->format(logger, level, event))) {
            if (!m_formatter->format(m_filestream, level, event)) {
                std::cout << "error" << std::endl;
            }
        }
    }

    std::string FileLogAppender::toYamlString() {
        MutexType::Lock lock(m_mutex);
        YAML::Node node;
        node["type"] = "FileLogAppender";
        node["file"] = m_filename;
        if (m_level != LogLevel::UNKNOWN) {
            node["level"] = LogLevel::ToString(m_level);
        }
        if (m_hasFormatter && m_formatter) {
            node["formatter"] = m_formatter->getPattern();
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }

    bool FileLogAppender::reopen() {
        MutexType::Lock lock(m_mutex);
        if (m_filestream) {
            m_filestream.close();
        }
        m_filestream.open(m_filename, std::ios::app);
        return !!m_filestream;
    }

    std::string StdoutLogAppender::toYamlString() {
        MutexType::Lock lock(m_mutex);
        YAML::Node node;
        node["name"] = "StdoutLogAppender";
        if (m_level != LogLevel::UNKNOWN) {
            node["level"] = LogLevel::ToString(m_level);
        }
        if (m_hasFormatter && m_formatter) {
            node["formatter"] = m_formatter->getPattern();
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }

    class MessageFormatItem : public LogFormatter::FormatterItem {
    public:
        MessageFormatItem(const std::string &fmt = "") {}

        void
        format(std::ostream &os, LogLevel::Level level, LogEvent::ptr event) override {
            os << event->getContent();
        }
    };

    class LevelFormatItem : public LogFormatter::FormatterItem {
    public:
        LevelFormatItem(const std::string &fmt = "") {}

        void
        format(std::ostream &os, LogLevel::Level level, LogEvent::ptr event) override {
            os << LogLevel::ToString(level);
        }
    };

    class ElapseFormatItem : public LogFormatter::FormatterItem {
    public:
        ElapseFormatItem(const std::string &fmt = "") {}

        void
        format(std::ostream &os, LogLevel::Level level, LogEvent::ptr event) override {
            os << event->getElapse();
        }
    };

    class ThreadIdFormatItem : public LogFormatter::FormatterItem {
    public:
        ThreadIdFormatItem(const std::string &fmt = "") {}

        void
        format(std::ostream &os, LogLevel::Level level, LogEvent::ptr event) override {
            os << event->getThreadId();
        }
    };

    class FiberIdFormatItem : public LogFormatter::FormatterItem {
    public:
        FiberIdFormatItem(const std::string &fmt = "") {}

        void
        format(std::ostream &os, LogLevel::Level level, LogEvent::ptr event) override {
            os << event->getFiberId();
        }
    };

    class DateTimeFormatItem : public LogFormatter::FormatterItem {
    public:
        DateTimeFormatItem(const std::string &format = "%Y-%m-%d %H:%M:%S") : m_format(format) {
            if (m_format.empty()) {
                m_format = "%Y-%m-%d %H:%M:%S";
            }
        }

        void
        format(std::ostream &os, LogLevel::Level level, LogEvent::ptr event) override {
            struct tm tm;
            time_t time = event->getTime();
            localtime_r(&time, &tm);
            char buf[64];
            strftime(buf, sizeof(buf), m_format.c_str(), &tm);
            os << buf;
        }

    private:
        std::string m_format;
    };

    class FilenameFormatItem : public LogFormatter::FormatterItem {
    public:
        FilenameFormatItem(const std::string &fmt = "") {}

        void
        format(std::ostream &os, LogLevel::Level level, LogEvent::ptr event) override {
            os << event->getFile();
        }
    };

    class LineFormatItem : public LogFormatter::FormatterItem {
    public:
        LineFormatItem(const std::string &fmt = "") {}

        void
        format(std::ostream &os, LogLevel::Level level, LogEvent::ptr event) override {
            os << event->getLine();
        }
    };

    class NewLineFormatItem : public LogFormatter::FormatterItem {
    public:
        NewLineFormatItem(const std::string &str = "") {}

        void format(std::ostream &os, LogLevel::Level level, LogEvent::ptr event) override {
            os << std::endl;
        }
    };

    class StringFormatItem : public LogFormatter::FormatterItem {
    public:
        StringFormatItem(const std::string &fmt) : m_string(fmt) {}

        void
        format(std::ostream &os, LogLevel::Level level, LogEvent::ptr event) override {
            os << m_string;
        }

    private:
        std::string m_string;
    };

    class TabFormatItem : public LogFormatter::FormatterItem {
    public:
        TabFormatItem(const std::string &str = "") {}

        void format(std::ostream &os, LogLevel::Level level, LogEvent::ptr event) override {
            os << "\t";
        }

    private:
        std::string m_string;
    };

    class NameFormatItem : public LogFormatter::FormatterItem {
    public:
        NameFormatItem(const std::string &str = "") {}

        void format(std::ostream &os, LogLevel::Level level, LogEvent::ptr event) override {
            os << event->getLogger()->getName();
        }
    };

    class ThreadNameFormatItem : public LogFormatter::FormatterItem {
    public:
        ThreadNameFormatItem(const std::string &str = "") {}

        void format(std::ostream &os, LogLevel::Level level, LogEvent::ptr event) override {
            os << event->getThreadName();
        }
    };

    LogFormatter::LogFormatter(const std::string &pattern) : m_pattern(pattern) {
        init();
    }

    std::string LogFormatter::format(LogLevel::Level level, LogEvent::ptr event) {
        std::stringstream ss;
        for (auto &item : m_items) {
            item->format(ss, level, event);
        }
        return ss.str();
    }

    std::ostream &LogFormatter::format(std::ostream &ofs, LogLevel::Level level, LogEvent::ptr event) {
        for (auto &i : m_items) {
            i->format(ofs, level, event);
        }
        return ofs;
    }

    void LogFormatter::init() {
        // str,format,type
        std::vector<std::tuple<std::string, std::string, int>> vec;
        std::string nstr;
        for (size_t i = 0; i < m_pattern.size(); ++i) {
            // 非%开头的格式，保留
            if (m_pattern[i] != '%') {
                nstr.append(1, m_pattern[i]);
                continue;
            }
            // %% ， 保留一个%
            if (i + 1 < m_pattern.size()) {
                if (m_pattern[i + 1] == '%') {
                    nstr.append(1, '%');
                    continue;
                }
            }
            size_t n = i + 1;
            // 1 ：当前字符串为解析格式
            int fmt_status = 0;
            size_t fmt_begin = 0;

            std::string str;
            std::string fmt;

            while (n < m_pattern.size()) {
                // 匹配中出现非字母，匹配结束
                if (!fmt_status && (!isalpha(m_pattern[n])) && m_pattern[n] != '{' && m_pattern[n] != '}') {
                    str = m_pattern.substr(i + 1, n - i - 1);
                    break;
                }
                if (fmt_status == 0) {
                    if (m_pattern[n] == '{') {  // 开启fmt的匹配
                        str = m_pattern.substr(i + 1, n - i - 1);
                        fmt_status = 1;
                        fmt_begin = n;
                        ++n;
                        continue;
                    }
                } else if (fmt_status == 1) {
                    if (m_pattern[n] == '}') { // 结束匹配， fmt去掉{和}
                        fmt = m_pattern.substr(fmt_begin + 1, n - fmt_begin - 1);
                        fmt_status = 0;
                        ++n;
                        break;
                    }
                }
                ++n;
                if (n == m_pattern.size()) {
                    if (str.empty()) {  // %+字母，取一个字母
                        str = m_pattern.substr(i + 1);
                    }
                }
            }
            if (fmt_status == 0) {
                if (!nstr.empty()) {
                    vec.push_back(std::make_tuple(nstr, std::string(), 0));
                    nstr.clear();
                }
                vec.push_back(std::make_tuple(str, fmt, 1));
                i = n - 1;
            } else if (fmt_status == 1) {
                std::cout << "pattern parse error: " << m_pattern << " - " << m_pattern.substr(i) << std::endl;
                m_error = true;
                vec.push_back(std::make_tuple("<<pattern_error>>", fmt, 0));
            }

        }
        if (!nstr.empty()) {
            vec.push_back(std::make_tuple(nstr, "", 0));
        }
        static std::map<std::string, std::function<FormatterItem::ptr(const std::string &fmt)>> s_format_items = {
#define XX(str, C) \
    {#str,[](const std::string &fmt){return FormatterItem::ptr(new C(fmt)); }}
                //{"m",[](const std::string &fmt){return FormatterItem::ptr(new MessageFormatItem(fmt)); }}
                XX(m, MessageFormatItem),           //m:消息
                XX(p, LevelFormatItem),             //p:日志级别
                XX(r, ElapseFormatItem),            //r:累计毫秒数
                XX(c, NameFormatItem),              //c:日志名称
                XX(t, ThreadIdFormatItem),          //t:线程id
                XX(n, NewLineFormatItem),           //n:换行
                XX(d, DateTimeFormatItem),          //d:时间
                XX(f, FilenameFormatItem),          //f:文件名
                XX(l, LineFormatItem),              //l:行号
                XX(T, TabFormatItem),               //T:Tab
                XX(F, FiberIdFormatItem),           //F:协程id
                XX(N, ThreadNameFormatItem),        //N:线程名称
#undef XX
        };
        for (auto &item : vec) {
            if (std::get<2>(item) == 0) {
                m_items.push_back(FormatterItem::ptr(new StringFormatItem(std::get<0>(item))));
            } else {
                auto it = s_format_items.find(std::get<0>(item));
                if (it == s_format_items.end()) {
                    m_items.push_back(
                            FormatterItem::ptr(new StringFormatItem("<<error_format %" + std::get<0>(item) + ">>")));
                    m_error = true;
                } else {
                    m_items.push_back(it->second(std::get<1>(item)));
                }
            }
//            std::cout << "(" << std::get<0>(item) << ") - (" << std::get<1>(item) << ") - (" << std::get<2>(item) << ")"
//                      << std::endl;
        }
    }


    LogManager::LogManager() {
        m_root.reset(new Logger);
        m_root->addAppender(LogAppender::ptr(new StdoutLogAppender));
        m_loggers[m_root->getName()] = m_root;
        init();
    }

    Logger::ptr LogManager::getLogger(const std::string &name) {
        MutexType::Lock lock(m_mutex);
        auto it = m_loggers.find(name);
        if (it != m_loggers.end()) {
            return it->second;
        }
        Logger::ptr logger(new Logger(name));
        logger->setRoot(m_root);
        m_loggers[name] = logger;
        return logger;
    }

    struct LogAppenderDefine {
        int type = 0; // 1 file, 2 std
        LogLevel::Level level = LogLevel::UNKNOWN;
        std::string formatter;
        std::string file;

        bool operator==(const LogAppenderDefine &rhs) const {
            return type == rhs.type
                   && level == rhs.level
                   && formatter == rhs.formatter
                   && file == rhs.file;
        }
    };

    struct LogDefine {
        std::string name;
        LogLevel::Level level = LogLevel::UNKNOWN;
        std::string formatter;
        std::vector<LogAppenderDefine> appenders;

        bool operator==(const LogDefine &rhs) const {
            return name == rhs.name
                   && level == rhs.level
                   && formatter == rhs.formatter
                   && appenders == rhs.appenders;
        }

        bool operator<(const LogDefine &rhs) const {
            return name < rhs.name;
        }

        bool isValid() const {
            return !name.empty();
        }
    };

    template<>
    class LexicalCast<std::string, LogDefine> {
    public:
        LogDefine operator()(const std::string &val) {
            YAML::Node node = YAML::Load(val);
            LogDefine ld;
            if (!node["name"].IsDefined()) {
                std::cout << "log config error: name is null, " << node << std::endl;
                throw std::logic_error("log config name is null");
            }
            ld.name = node["name"].as<std::string>();
            ld.level = LogLevel::FromString(node["level"].IsDefined() ? node["level"].as<std::string>() : "");
            if (node["formatter"].IsDefined()) {
                ld.formatter = node["formatter"].as<std::string>();
            }
            if (node["appenders"].IsDefined()) {
                for (size_t x = 0; x < node["appenders"].size(); ++x) {
                    auto a = node["appenders"][x];
                    if (!a["type"].IsDefined()) {
                        std::cout << "log config error: appender type is null, " << a << std::endl;
                        continue;
                    }
                    std::string type = a["type"].as<std::string>();
                    LogAppenderDefine lad;
                    if (type == "FileLogAppender") {
                        lad.type = 1;
                        if (!a["file"].IsDefined()) {
                            std::cout << "log config error: fileappender file is null, " << a << std::endl;
                            continue;
                        }
                        lad.file = a["file"].as<std::string>();
                        if (a["formatter"].IsDefined()) {
                            lad.formatter = a["formatter"].as<std::string>();
                        }
                    } else if (type == "StdoutLogAppender") {
                        lad.type = 2;
                        if (a["formatter"].IsDefined()) {
                            lad.formatter = a["formatter"].as<std::string>();
                        }
                    } else {
                        std::cout << "log config error: appender type is invalid, " << a << std::endl;
                        continue;
                    }
                    if (a["level"].IsDefined()) {
                        lad.level = LogLevel::FromString(a["level"].as<std::string>());
                    }
                    ld.appenders.push_back(lad);
                }
            }
            return ld;
        }
    };

    template<>
    class LexicalCast<LogDefine, std::string> {
    public:
        std::string operator()(const LogDefine &val) {
            YAML::Node node;
            node["name"] = val.name;
            if (val.level != LogLevel::UNKNOWN) {
                node["level"] = LogLevel::ToString(val.level);
            }
            if (!val.formatter.empty()) {
                node["formatter"] = val.formatter;
            }
            for (auto &a : val.appenders) {
                YAML::Node na;
                if (a.type == 1) {
                    na["type"] = "FileLogAppender";
                    na["file"] = a.file;
                } else if (a.type == 2) {
                    na["type"] = "StdoutLogAppender";
                }
                if (a.level != LogLevel::UNKNOWN) {
                    na["level"] = LogLevel::ToString(a.level);
                }
                if (!a.formatter.empty()) {
                    na["formatter"] = val.formatter;
                }
                node["appenders"].push_back(na);
            }
            std::stringstream ss;
            ss << node;
            return ss.str();
        }
    };

    sylar::ConfigVar<std::set<LogDefine>>::ptr g_log_defines =
            sylar::Config::Lookup("logs", std::set<LogDefine>(), "logs config");

    struct LogIniter {
        LogIniter() {
            g_log_defines->addListener([](const std::set<LogDefine> old_value, const std::set<LogDefine> new_value) {
                SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "on_logger_conf_change";
                for (auto &i:new_value) {
                    auto it = old_value.find(i);
                    Logger::ptr logger;
                    if (it == old_value.end()) {
                        logger = SYLAR_LOG_NAME(i.name);
                    } else {
                        if (!(i == *it)) {
                            logger = SYLAR_LOG_NAME(i.name);
                        } else {
                            continue;
                        }
                    }
                    logger->setLevel(i.level);
                    if (!i.formatter.empty()) {
                        logger->setFormatter(i.formatter);
                    }
                    logger->clearAppenders();
                    for (auto &a : i.appenders) {
                        LogAppender::ptr ap;
                        if (a.type == 1) {
                            ap.reset(new FileLogAppender(a.file));
                        } else if (a.type == 2) {
                            ap.reset(new StdoutLogAppender);
                        }
                        ap->setLevel(a.level);
                        if (!a.formatter.empty()) {
                            LogFormatter::ptr fmt(new LogFormatter(a.formatter));
                            if (!fmt->isError()) {
                                ap->setFormatter(fmt);
                            } else {
                                std::cout << "log.name=" << i.name << " appender type="
                                          << a.type
                                          << " formatter=" << a.formatter << " is invalid"
                                          << std::endl;
                            }
                        }
                        logger->addAppender(ap);
                    }
                }
                for (auto &i : old_value) {
                    auto it = new_value.find(i);
                    if (it == new_value.end()) {
                        auto logger = SYLAR_LOG_NAME(i.name);
                        logger->setLevel(LogLevel::UNKNOWN);
                        logger->clearAppenders();
                    }
                }
            });
        }
    };

    static LogIniter __log_init;

    std::string LogManager::toYamlString() {
        MutexType::Lock lock(m_mutex);
        YAML::Node node;
        for (auto &i : m_loggers) {
            node.push_back(YAML::Load(i.second->toYamlString()));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }

    void LogManager::init() {
    }
};



